﻿#include "modbusRtu.h"

#include <QModbusRtuSerialMaster>
#include <QModbusDataUnit>
#include <QModbusReply>
#include <QVariant>
#include <QSerialPort>
#include <QDebug>

#define REAL_TIME_REFRSH_INTERVAL_MS 1000 // 实时刷新时间间隔ms


Modbus::Modbus(QObject *parent) : QObject(parent)
{
//    qRegisterMetaType<QSerialPort::SerialPortError>("Turing_Serial_DataAnalysis");

    /* 1. 创建QModbusDevice对象 */
    m_modbusDevice = new QModbusRtuSerialMaster;

    /* 2. 如果处于连接状态，则断开连接 */
    if (m_modbusDevice->state() == QModbusDevice::ConnectedState)
    {
        // 断开连接设备
        m_modbusDevice->disconnectDevice();
        emit disconnect();
    }

    /* 3. 设置串口默认参数 */
    // 设置校验 无校验
    m_modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter, QSerialPort::NoParity);
    // 设置停止位
    m_modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, QSerialPort::OneStop);
    // 设置数据位
    m_modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, QSerialPort::Data8);

    /* 4. 设置其他信息 */
    // 设置超时时间
    m_modbusDevice->setTimeout(50);
    // 设置失败重试次数
    m_modbusDevice->setNumberOfRetries(1);



    /* 实例化实时刷新定时器 */
    m_realTimeRefrsh = new QTimer(this);
    connect(m_realTimeRefrsh, &QTimer::timeout, this, [=](){
        modbusGetRegs(MODBUS_REG_GET_MOTOR_DATA);
        modbusGetRegs(MODBUS_REG_GET_ALOG_DATA);
    });
}

Modbus::~Modbus()
{
    if (m_modbusDevice->state() == QModbusDevice::ConnectedState)
    {
        m_modbusDevice->disconnectDevice();
        emit disconnect();
        qDebug() << "断开连接设备";
    }
}

void Modbus::searchAvailablePorts()
{
    // 可用串口列表
    QStringList portList;

    // 查询可用串口
    foreach (const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
    {
        // 临时串口句柄
        QSerialPort portTemp;
        portTemp.setPort(info);
        //以可读可写的方式打开(临时打开，扫描可用串口)
        if(portTemp.open(QIODevice::ReadWrite))
        {
            //将扫描到的可用串口写入列表
            portList.append(portTemp.portName());
            //关闭串口
            portTemp.close();
        }
    }

    // 刷新可用串口
    emit refrAvailablePorts(portList);
}

void Modbus::setModbusParam(ModbusRtuParam_t param)
{
    qDebug() << "设置Modbus参数";
    m_modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter, QVariant(param.port));
    m_modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, QVariant(param.baud));

    // 设置数据位
    switch (param.dataBit)
    {
    case 0: m_modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, QSerialPort::Data8); break;
    case 1: m_modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, QSerialPort::Data7); break;
    case 2: m_modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, QSerialPort::Data6); break;
    case 3: m_modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, QSerialPort::Data5); break;
    }

    // 设置停止位
    switch (param.stopBit)
    {
    case 0: m_modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, QSerialPort::OneStop);        break;
    case 1: m_modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, QSerialPort::OneAndHalfStop); break;
    case 2: m_modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, QSerialPort::TwoStop);        break;
    }

    // 设置校验位
    switch (param.Parity)
    {
    case 0: m_modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter, QSerialPort::NoParity);    break;
    case 1: m_modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter, QSerialPort::OddParity);   break;
    case 2: m_modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter, QSerialPort::EvenParity);  break;
    case 3: m_modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter, QSerialPort::MarkParity);  break;
    case 4: m_modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter, QSerialPort::SpaceParity); break;
    }
}


void Modbus::setModbusEnable(bool en, QString port, QString baud)
{
    if (en)
    {
        m_modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter, QVariant(port));
        m_modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, QVariant(baud));
        qInfo() << port << ": " << baud;

        if (!m_modbusDevice->connectDevice())
        {
            qWarning() << "连接到串口失败：" << m_modbusDevice->errorString();
        }
        else
        {
            qInfo() << "连接到串口成功";

            refrInfo();

            m_realTimeRefrsh->start(REAL_TIME_REFRSH_INTERVAL_MS);
        }
    }
    else
    {

        {if (m_modbusDevice->state() == QModbusDevice::ConnectedState)
            qInfo() << "断开连接设备";
            m_realTimeRefrsh->stop();
            m_modbusDevice->disconnectDevice();
            emit disconnect();
        }
    }
}

void Modbus::refrInfo()
{
    modbusGetRegs(MODBUS_REG_GET_SYSTEM);
    modbusGetRegs(MODBUS_REG_GET_MOTOR_CTRL_M);
    modbusGetRegs(MODBUS_REG_GET_MOTOR_CTRL_S);
    modbusGetRegs(MODBUS_REG_GET_APP);
    modbusGetRegs(MODBUS_REG_GET_MOTOR_DATA);
    modbusGetRegs(MODBUS_REG_GET_ALOG_DATA);
    modbusGetRegs(MODBUS_REG_GET_CMD);
    modbusGetRegs(MODBUS_REG_GET_DNA);
}

void Modbus::modbusGetRegs(ModbusRegCmd_e type)
{
    QModbusDataUnit data(QModbusDataUnit::HoldingRegisters, g_regMap[type].regAddr, g_regMap[type].regNum);

    QModbusReply *reply = m_modbusDevice->sendReadRequest(data, m_maddr);
    if (nullptr == reply)
    {
        qDebug() << "发送请求数据失败: " << m_modbusDevice->errorString();
    }
    else
    {
        if (!reply->isFinished())
        {
            connect(reply, &QModbusReply::finished, this, [this, reply, type](){
                onReadReady(reply, type);
            });
        }
        else
        {
            delete reply;
        }
    }
}

void Modbus::modbusGetRegs(ModbusRegCmd_e type, int regAddr, quint16 regNum)
{
    QModbusDataUnit data(QModbusDataUnit::HoldingRegisters, regAddr, regNum);

    QModbusReply *reply = m_modbusDevice->sendReadRequest(data, m_maddr);
    if (nullptr == reply)
    {
        qDebug() << "发送请求数据失败: " << m_modbusDevice->errorString();
    }
    else
    {
        if (!reply->isFinished())
        {
            connect(reply, &QModbusReply::finished, this, [this, reply, type](){
                onReadReady(reply, type);
            });
        }
        else
        {
            delete reply;
        }
    }
}

void Modbus::modbusSetRegs(ModbusRegCmd_e type, QModbusDataUnit writeData)
{
    if (m_modbusDevice->state() != QModbusDevice::ConnectedState)
    {
        emit isUnenable();
        return;
    }

    // 发送数据
    QModbusReply* reply = m_modbusDevice->sendWriteRequest(writeData, m_maddr);

    if (reply)
    {
        if (!reply->isFinished())
        {
            //接收响应信息
            connect(reply, &QModbusReply::finished, this, [=](){
                bool ret = false;

                if (reply->error() == QModbusDevice::ProtocolError)
                {
                    //接收到的响应信息是协议错误
                    qDebug() << "写入数据错误：" << reply->errorString();
                }
                else if (reply->error() != QModbusDevice::NoError)
                {
                    //接收到的响应消息是其它错误
                    qDebug() << "写入数据错误： " << reply->errorString();
                }
                else
                {
                    //接收到的消息没有错误 一般没有必要解析响应消息
                    const QModbusDataUnit data = reply->result();

                    qDebug() << "消息数据个数：" << data.valueCount() << " :" << data.values();
                    ret = true;
                }

                emit modbusWriteFeedback(type, ret);
                reply->deleteLater();
            });
        }
        else
        {
            //发送没有响应数据
            reply->deleteLater();
        }
    }
    else
    {
        qDebug() << "sendWriteRequest Error: " << reply->errorString();
    }
}

void Modbus::onReadReady(QModbusReply *reply, ModbusRegCmd_e type)
{
    QModbusDataUnit responseData;

    //判断是否出错
    if (reply->error() == QModbusDevice::NoError)
    {
        //读取响应数据
        responseData = reply->result();

        // 数据处理
        emit dataReady(type, responseData);
    }
    else if (reply->error() == QModbusDevice::ProtocolError)
    {
        qDebug() << "Read Response Protocol Error: " << reply->errorString();
    }
    else
    {
        qDebug() << "Read Response Error: " << reply->errorString();
    }

    //删除reply
    reply->deleteLater();
}

void Modbus::setMaddr(const quint8 &maddr)
{
    m_maddr = maddr;
    QMessageBox::information((QWidget*)this->parent(), "设置成功", QString("设备Modbus地址设置为：0x%1").arg(m_maddr, 2, 16, QLatin1Char('0')));
}
